home *** CD-ROM | disk | FTP | other *** search
/ QuickTime 2.0 Developer Kit / QuickTime 2.0 Developer Kit.iso / mac / MAC / System Extensions / Macintosh Drag and Drop 1.1.1 / Demo Applications / DragText Sources / drag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-15  |  21.9 KB  |  959 lines  |  [TEXT/KAHL]

  1. /*
  2.  *
  3.  *        drag.c
  4.  *
  5.  *        Text dragging routines. The code in this file implements the text
  6.  *        dragging features of this program. This is the single most important
  7.  *        file in the DragText program. The last function in this file,
  8.  *        DragText is called when a mouseDown is detected in the hilite region
  9.  *        of the current TextEdit field. To follow the text dragging code,
  10.  *        start with the DragText function.
  11.  *
  12.  *
  13.  *        Author:        Rob Johnston
  14.  *        Date:        Friday, September 10, 1993
  15.  *
  16.  *        Copyright © 1992-93 Apple Computer, Inc.
  17.  *
  18.  */
  19.  
  20. #include <QDOffscreen.h>
  21. #include <Folders.h>
  22. #include <Drag.h>
  23.  
  24. #include "globals.h"
  25. #include "prototypes.h"
  26. #include "resources.h"
  27. #include "Offscreen.h"
  28.  
  29.  
  30. //
  31. //    Can use custom drawing procedure.
  32. //
  33.  
  34. //    #define USE_CUSTOM_DRAWING
  35.  
  36.  
  37.     static long            caretTime;
  38.     static short        caretOffset, caretShow, lastOffset, insertPosition, canAcceptItems;
  39.     static short        cursorInContent;
  40.  
  41.  
  42. /*
  43.  *    The following #define statement provides access to TextEdit's caretTime.
  44.  */
  45.  
  46. #define    gCaretTime        ((short) *((long *) 0x02F4))
  47.  
  48.  
  49. /*
  50.  *    MyDrawingProc
  51.  *
  52.  *    Simple drawing proc for dragging text. This drawing procedure is very similar to
  53.  *    the Drag Manager's built in drawing proc, except that it uses a solid black pattern
  54.  *    to draw the region instead of using the dithered gray pattern.
  55.  */
  56.  
  57. #ifdef USE_CUSTOM_DRAWING
  58.  
  59. pascal OSErr MyDrawingProc(DragRegionMessage message,
  60.                            RgnHandle showRgn, Point showOrigin,
  61.                            RgnHandle hideRgn, Point hideOrigin,
  62.                            void *dragDrawingRefCon, DragReference theDragRef)
  63.  
  64. {    OSErr            result = paramErr;
  65.     RgnHandle        tempRgn;
  66.  
  67.     switch(message) {
  68.  
  69.         case dragRegionBegin:
  70.  
  71.             //
  72.             //    No initialization necessary for our drawing proc. Make sure noErr
  73.             //    is returned, otherwise the Drag Manager will revert back to it's
  74.             //    built in drawing proc.
  75.             //
  76.  
  77.             result = noErr;
  78.             break;
  79.  
  80.         case dragRegionDraw:
  81.  
  82.             //
  83.             //    Find the difference between the region needed to be shown and the
  84.             //    region needed to be hidden. Inverting the difference removes the pixels
  85.             //    that must be hidden and shows the pixels that must be shown in one step.
  86.             //
  87.  
  88.             XorRgn(showRgn, hideRgn, tempRgn = NewRgn());
  89.             InvertRgn(tempRgn);
  90.             DisposeRgn(tempRgn);
  91.             result = noErr;
  92.             break;
  93.  
  94.         case dragRegionHide:
  95.  
  96.             //
  97.             //    Simply hide the region given to us by inverting it.
  98.             //
  99.  
  100.             InvertRgn(hideRgn);
  101.             result = noErr;
  102.             break;
  103.     }
  104.  
  105.     return(result);
  106. }
  107.  
  108. #endif USE_CUSTOM_DRAWING
  109.  
  110.  
  111. /*
  112.  *    Given a point in global coordinates, HitTest returns a pointer to a
  113.  *    document structure if the point is inside a document window on the screen.
  114.  *    If the point is not inside a document window, HitTest return NULL in
  115.  *    theDoc. If the point is in a doument window and also in the viewRect of
  116.  *    the document's TextEdit field, HitTest also returns the offset into
  117.  *    the text that corresponds to that point. If the point is not in the text,
  118.  *    HitTest returns -1.
  119.  */
  120.  
  121. short HitTest(Point theLoc, Document **theDoc)
  122.  
  123. {    WindowPtr        theWindow;
  124.     short            offset;
  125.  
  126.     *theDoc = 0L;
  127.     offset = -1;
  128.  
  129.     if (FindWindow(theLoc, &theWindow) == inContent) {
  130.         if (*theDoc = IsDocumentWindow(theWindow)) {
  131.  
  132.             SetPort(theWindow);
  133.             GlobalToLocal(&theLoc);
  134.  
  135.             if ((PtInRect(theLoc, &(**((**theDoc).theTE)).viewRect)) && 
  136.                 (PtInRect(theLoc, &(**((**theDoc).theTE)).destRect))) {
  137.  
  138.                 offset = TEGetOffset(theLoc, (**theDoc).theTE);
  139.  
  140.                 if ((TEIsFrontOfLine(offset, (**theDoc).theTE)) && (offset) &&            
  141.                         ((*((**((**theDoc).theTE)).hText))[offset - 1] != 0x0D) &&
  142.                         (TEGetPoint(offset - 1, (**theDoc).theTE).h < theLoc.h)) {
  143.                     offset--;
  144.                 }
  145.             }
  146.         }
  147.     }
  148.  
  149.     return(offset);
  150. }
  151.  
  152.  
  153. /*
  154.  *    DrawCaret draws a caret in a TextEdit field at the given offset. DrawCaret
  155.  *    expects the port to be set to the port that the TextEdit field is in.
  156.  *    DrawCaret inverts the image of the caret onto the screen.
  157.  */
  158.  
  159. short DrawCaret(short offset, TEHandle theTE)
  160.  
  161. {    Point        theLoc;
  162.     short        theLine, lineHeight;
  163.  
  164.     /*
  165.      *    Get the coordinates and the line of the offset to draw the caret.
  166.      */
  167.  
  168.     theLoc  = TEGetPoint(offset, theTE);
  169.     theLine = TEGetLine(offset, theTE);
  170.  
  171.     /*
  172.      *    For some reason, TextEdit dosen't return the proper coordinates
  173.      *    of the last offset in the field if the last character in the record
  174.      *    is a carriage return. TEGetPoint returns a point that is one line
  175.      *    higher than expected. The following code fixes this problem.
  176.      */
  177.  
  178.     if ((offset == (**theTE).teLength) &&
  179.             (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D) {
  180.         theLoc.v += TEGetHeight(theLine, theLine, theTE);
  181.     }
  182.  
  183.     /*
  184.      *    Always invert the caret when drawing.
  185.      */
  186.  
  187.     PenMode(patXor);
  188.  
  189.     /*
  190.      *    Get the height of the line that the offset points to.
  191.      */
  192.  
  193.     lineHeight = TEGetHeight(theLine, theLine, theTE);
  194.  
  195.     /*
  196.      *    Draw the appropriate caret image.
  197.      */
  198.  
  199.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  200.     Line(0, 1 - lineHeight);
  201.  
  202.     PenNormal();
  203. }
  204.  
  205.  
  206. char GetCharAtOffset(short offset, TEHandle theTE)
  207.  
  208. {
  209.     if (offset < 0)
  210.         return(0x0D);
  211.  
  212.     return(((char *) *((**theTE).hText))[offset]);
  213. }
  214.  
  215.  
  216. Boolean WhiteSpace(char theChar)
  217.  
  218. {
  219.     return((theChar == ' ') || (theChar == 0x0D));
  220. }
  221.  
  222.  
  223. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  224.  
  225. {    char        theChar;
  226.  
  227.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  228.         return(true);
  229.  
  230.     theChar = ((char *) *((**theTE).hText))[offset];
  231.     return((theChar == ' ') || (theChar == 0x0D));
  232. }
  233.  
  234.  
  235. void InsertTextAtOffset(short offset, char *theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  236.  
  237. {
  238.     if (size == 0)
  239.         return;
  240.  
  241.     //
  242.     //    If inserting at the end of a word and the selection does not begin with
  243.     //    a space, insert a space before the insertion.
  244.     //
  245.  
  246.     if (!WhiteSpaceAtOffset(offset - 1, theTE) &&
  247.          WhiteSpaceAtOffset(offset, theTE) &&
  248.         !WhiteSpace(theBuf[0])) {
  249.  
  250.         TESetSelect(offset, offset, theTE);
  251.         TEKey(' ', theTE);
  252.         offset++;
  253.     }
  254.  
  255.     //
  256.     //    If inserting at the beginning of a word and the selection does not end
  257.     //    with a space, insert a space after the insertion.
  258.     //
  259.  
  260.     if ( WhiteSpaceAtOffset(offset - 1, theTE) &&
  261.         !WhiteSpaceAtOffset(offset, theTE) &&
  262.         !WhiteSpace(theBuf[size - 1])) {
  263.  
  264.         TESetSelect(offset, offset, theTE);
  265.         TEKey(' ', theTE);
  266.     }
  267.  
  268.     TESetSelect(offset, offset, theTE);
  269.     TEStylInsert(theBuf, size, theStyl, theTE);
  270.     TESetSelect(offset, offset + size, theTE);
  271. }
  272.  
  273.  
  274. short GetSelectionSize(Document *theDocument)
  275.  
  276. {
  277.     return((**(theDocument->theTE)).selEnd - (**(theDocument->theTE)).selStart);
  278. }
  279.  
  280.  
  281. Ptr GetSelectedTextPtr(Document *theDocument)
  282.  
  283. {
  284.     return((*(**(theDocument->theTE)).hText) + (**(theDocument->theTE)).selStart);
  285. }
  286.  
  287.  
  288. /*
  289.  *    MySendDataProc
  290.  *
  291.  *    Will provide 'styl' data for the drag when requested.
  292.  */
  293.  
  294. pascal OSErr MySendDataProc(FlavorType theType, void *refCon,
  295.                             ItemReference theItem, DragReference theDrag)
  296.  
  297. {    Document        *theDocument = (Document *) refCon;
  298.     StScrpHandle    theStyl;
  299.  
  300.     if (theType == 'styl') {
  301.  
  302.         theStyl = GetStylScrap(theDocument->theTE);
  303.  
  304.         //
  305.         //    Call SetDragItemFlavorData to provide the requested data.
  306.         //
  307.  
  308.         HLock((Handle) theStyl);
  309.         SetDragItemFlavorData(theDrag, theItem, 'styl', (Ptr) *theStyl,
  310.                               GetHandleSize((Handle) theStyl), 0L);
  311.         HUnlock((Handle) theStyl);
  312.         DisposeHandle((Handle) theStyl);
  313.  
  314.     } else {
  315.  
  316.         return(badDragFlavorErr);
  317.  
  318.     }
  319.  
  320.     return(noErr);
  321. }
  322.  
  323.  
  324. /*
  325.  *    MyReceiveDropHandler
  326.  *
  327.  *    Called by the Drag Manager when a drop occurs over one of the DragText document windows.
  328.  */
  329.  
  330. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon,
  331.                                   DragReference theDrag)
  332.  
  333. {    OSErr                result;
  334.     TEHandle            tempTE;
  335.     Rect                theRect, srcRect;
  336.     unsigned short        items, index;
  337.     ItemReference        theItem;
  338.     DragAttributes        attributes;
  339.     Ptr                    textData;
  340.     StScrpHandle        stylHandle;
  341.     Size                textSize, stylSize;
  342.     short                offset, selStart, selEnd, mouseDownModifiers, mouseUpModifiers, moveText;
  343.     Document            *theDocument = (Document *) handlerRefCon;
  344.     WindowOffscreen        *theOffscreen;
  345.     Point                thePoint;
  346.  
  347.     if ((!canAcceptItems) || (insertPosition == -1))
  348.         return(dragNotAcceptedErr);
  349.  
  350.     SetPort(theWindow);
  351.  
  352.     GetDragAttributes(theDrag, &attributes);
  353.     GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  354.  
  355.     moveText = (attributes & dragInsideSenderWindow) &&
  356.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  357.  
  358.     //
  359.     //    Loop through all of the drag items contained in this drag and collect the text
  360.     //    into the tempTE record.
  361.     //
  362.  
  363.     SetRect(&theRect, 0, 0, 0, 0);
  364.     tempTE = TEStylNew(&theRect, &theRect);
  365.  
  366.     CountDragItems(theDrag, &items);
  367.  
  368.     for (index = 1; index <= items; index++) {
  369.  
  370.         //
  371.         //    Get the item's reference number, so we can refer to it.
  372.         //
  373.  
  374.         GetDragItemReferenceNumber(theDrag, index, &theItem);
  375.  
  376.         //
  377.         //    Try to get the flags for a 'TEXT' flavor. If this returns noErr,
  378.         //    then we know that a 'TEXT' flavor exists in the item.
  379.         //
  380.  
  381.         result = GetFlavorDataSize(theDrag, theItem, 'TEXT', &textSize);
  382.  
  383.         if (result == noErr) {
  384.  
  385.             textData = NewPtr(textSize);
  386.             if (textData == 0L) {
  387.                 TEDispose(tempTE);
  388.                 return(memFullErr);
  389.             }
  390.  
  391.             GetFlavorData(theDrag, theItem, 'TEXT', textData, &textSize, 0L);
  392.  
  393.             //
  394.             //    Check for optional styl data for the TEXT.
  395.             //
  396.  
  397.             stylHandle = 0L;
  398.             result = GetFlavorDataSize(theDrag, theItem, 'styl', &stylSize);
  399.             if (result == noErr) {
  400.  
  401.                 stylHandle = (StScrpHandle) NewHandle(stylSize);
  402.                 if (stylHandle == 0L) {
  403.                     TEDispose(tempTE);
  404.                     DisposePtr(textData);
  405.                     return(memFullErr);
  406.                 }
  407.  
  408.                 HLock((Handle) stylHandle);
  409.                 GetFlavorData(theDrag, theItem, 'styl', *stylHandle, &stylSize, 0L);
  410.                 HUnlock((Handle) stylHandle);
  411.  
  412.             }
  413.  
  414.             //
  415.             //    Insert this drag item's text into the tempTE.
  416.             //
  417.  
  418.             TESetSelect(32767, 32767, tempTE);
  419.             TEStylInsert(textData, textSize, stylHandle, tempTE);
  420.  
  421.             DisposePtr(textData);
  422.             if (stylHandle)
  423.                 DisposeHandle((Handle) stylHandle);
  424.         }
  425.     }
  426.  
  427.     //
  428.     //    Pull the TEXT and styl data out of the tempTE handle.
  429.     //
  430.  
  431.     textData = NewPtr(textSize = (**tempTE).teLength);
  432.     if (textData == 0L) {
  433.         TEDispose(tempTE);
  434.         return(memFullErr);
  435.     }
  436.     BlockMove(*(**tempTE).hText, textData, textSize);
  437.  
  438.     TESetSelect(0, 32767, tempTE);
  439.     stylHandle = GetStylScrap(tempTE);
  440.  
  441.     TEDispose(tempTE);
  442.  
  443.     //
  444.     //    If we actually received text, insert it into the destination.
  445.     //
  446.  
  447.     if (textSize != 0) {
  448.  
  449.         //
  450.         //    If the caret or highlighting is on the screen, remove it/them.
  451.         //
  452.  
  453.         offset = caretOffset;
  454.  
  455.         if (caretOffset != -1) {
  456.             DrawCaret(caretOffset, theDocument->theTE);
  457.             caretOffset = -1;
  458.         }
  459.  
  460.         if (attributes & dragHasLeftSenderWindow) {
  461.             HideDragHilite(theDrag);
  462.         }
  463.  
  464.         //
  465.         //    If the drag occurred completely within the same window and the window is not
  466.         //    frontmost, bring the window forward and update its contents before completing
  467.         //    the drag.
  468.         //
  469.  
  470.         if ((attributes & dragInsideSenderWindow) && (theDocument->theWindow != FrontWindow())) {
  471.             SelectWindow(theDocument->theWindow);
  472.             UpdateWindow(theDocument);
  473.             TEActivate(theDocument->theTE);
  474.         }
  475.  
  476.         //
  477.         //    Draw everything into offscreen pixmap.
  478.         //
  479.  
  480.         if (theOffscreen = DrawOffscreen(theDocument->theWindow)) {
  481.             (**(theDocument->theTE)).inPort = (GrafPtr) theOffscreen->offscreenWorld;
  482.         }
  483.  
  484.         //
  485.         //    If the window is not active, must activate TE before inserting
  486.         //    text or the background hilite will not update correctly.
  487.         //
  488.  
  489.         if (!((WindowPeek) theDocument->theWindow)->hilited) {
  490.             TEActivate(theDocument->theTE);
  491.         }
  492.  
  493.         //
  494.         //    If this window is also the sender, delete source selection if no
  495.         //    option key.
  496.         //
  497.  
  498.         if (moveText) {
  499.             selStart = (**(theDocument->theTE)).selStart;
  500.             selEnd   = (**(theDocument->theTE)).selEnd;
  501.             if ( WhiteSpaceAtOffset(selStart - 1, theDocument->theTE) &&
  502.                 !WhiteSpaceAtOffset(selStart, theDocument->theTE) &&
  503.                 !WhiteSpaceAtOffset(selEnd - 1, theDocument->theTE) &&
  504.                  WhiteSpaceAtOffset(selEnd, theDocument->theTE)) {
  505.                  
  506.                  if (GetCharAtOffset(selEnd, theDocument->theTE) == ' ')
  507.                     (**(theDocument->theTE)).selEnd++;
  508.             }
  509.             if (insertPosition > selStart) {
  510.                 insertPosition -= ((**(theDocument->theTE)).selEnd -
  511.                                    (**(theDocument->theTE)).selStart);
  512.             }
  513.             srcRect = (**theDocument->hiliteRgn).rgnBBox;
  514.             TEDelete(theDocument->theTE);
  515.         }
  516.  
  517.         InsertTextAtOffset(insertPosition, textData, textSize, stylHandle, theDocument->theTE);
  518.  
  519.         TEGetHiliteRgn(theDocument->hiliteRgn, theDocument->theTE);
  520.  
  521.         //
  522.         //    If the text is moving (not copying) within the same window, provide a ZoomRects
  523.         //    from the source to the destination before revealing the reflowed text.
  524.         //
  525.  
  526.         if (moveText) {
  527.             theRect = (**theDocument->hiliteRgn).rgnBBox;
  528.             thePoint.h = thePoint.v = 0;
  529.             SetPort(theWindow);
  530.             LocalToGlobal(&thePoint);
  531.             OffsetRect(&srcRect, thePoint.h, thePoint.v);
  532.             OffsetRect(&theRect, thePoint.h, thePoint.v);
  533.             ZoomRects(&srcRect, &theRect, 12, zoomDecelerate);
  534.         }
  535.  
  536.         theDocument->dirty = true;
  537.     }
  538.  
  539.     DisposePtr(textData);
  540.  
  541.     if (stylHandle)
  542.         DisposeHandle((Handle) stylHandle);
  543.  
  544.     //
  545.     //    Undo the TEActivate, if needed.
  546.     //
  547.  
  548.     if (!((WindowPeek) theDocument->theWindow)->hilited) {
  549.         TEDeactivate(theDocument->theTE);
  550.     }
  551.  
  552.     //
  553.     //    Show the offscreen bitmap.
  554.     //
  555.  
  556.     DrawOnscreen(theOffscreen);
  557.     (**(theDocument->theTE)).inPort = theDocument->theWindow;
  558.  
  559.     return(noErr);
  560. }
  561.  
  562.  
  563. /*
  564.  *    MyTrackingHandler
  565.  *
  566.  *    This is the drag tracking handler for windows in the DragText application.
  567.  */
  568.  
  569. pascal OSErr MyTrackingHandler(short message, WindowPtr theWindow,
  570.                                void *handlerRefCon, DragReference theDrag)
  571.  
  572. {    short                result, offset;
  573.     long                theTime = TickCount();
  574.     unsigned short        count, index;
  575.     unsigned long        flavorFlags, attributes;
  576.     ItemReference        theItem;
  577.     RgnHandle            theRgn;
  578.     Document            *theDocument = (Document *) handlerRefCon;
  579.     Document            *hitDoc;
  580.     Point                theMouse, localMouse;
  581.  
  582.     if ((message != dragTrackingEnterHandler) && (!canAcceptItems))
  583.         return(noErr);
  584.  
  585.     GetDragAttributes(theDrag, &attributes);
  586.  
  587.     switch (message) {
  588.  
  589.         case dragTrackingEnterHandler:
  590.  
  591.             //
  592.             //    We get called with this message the first time that a drag enters ANY
  593.             //    window in our application. Check to see if all of the drag items contain
  594.             //    TEXT. We only accept a drag if all of the items in the drag can be accepted.
  595.             //
  596.  
  597.             canAcceptItems = true;
  598.  
  599.             CountDragItems(theDrag, &count);
  600.  
  601.             for (index = 1; index <= count; index++) {
  602.                 GetDragItemReferenceNumber(theDrag, index, &theItem);
  603.  
  604.                 result = GetFlavorFlags(theDrag, theItem, 'TEXT', &flavorFlags);
  605.  
  606.                 if (result != noErr) {
  607.                     canAcceptItems = false;
  608.                     break;
  609.                 }
  610.             }
  611.  
  612.             break;
  613.  
  614.         case dragTrackingEnterWindow:
  615.  
  616.             //
  617.             //    We receive an EnterWindow message each time a drag enters one of our
  618.             //    application's windows. We initialize our global variables for tracking
  619.             //    the drag through the window.
  620.             //
  621.  
  622.             caretTime = theTime;
  623.             caretOffset = lastOffset = -1;
  624.             caretShow = true;
  625.  
  626.             cursorInContent = false;
  627.  
  628.             break;
  629.  
  630.         case dragTrackingInWindow:
  631.  
  632.             //
  633.             //    We receive InWindow messages as long as the mouse is in one of our windows
  634.             //    during a drag. We draw the window highlighting and blink the insertion caret
  635.             //    when we get these messages.
  636.             //
  637.  
  638.             GetDragMouse(theDrag, &theMouse, 0L);
  639.             localMouse = theMouse;
  640.             GlobalToLocal(&localMouse);
  641.  
  642.             //
  643.             //    Show or hide the window highlighting when the mouse enters or leaves the
  644.             //    TextEdit field in our window (we don't want to show the highlighting when
  645.             //    the mouse is over the window title bar or over the scroll bars).
  646.             //
  647.  
  648.             if (attributes & dragHasLeftSenderWindow) {
  649.                 if (PtInRect(localMouse, &(**(theDocument->theTE)).viewRect)) {
  650.  
  651.                     if (!cursorInContent) {
  652.                         RectRgn(theRgn = NewRgn(), &(**(theDocument->theTE)).viewRect);
  653.                         ShowDragHilite(theDrag, theRgn, true);
  654.                         DisposeRgn(theRgn);
  655.                     }
  656.                     cursorInContent = true;
  657.  
  658.                 } else {
  659.  
  660.                     if (cursorInContent) {
  661.                         HideDragHilite(theDrag);
  662.                     }
  663.                     cursorInContent = false;
  664.  
  665.                 }
  666.             }
  667.  
  668.             offset = HitTest(theMouse, &hitDoc);
  669.  
  670.             //
  671.             //    If this application is the sender, do not allow tracking through
  672.             //    the selection in the window that sourced the drag.
  673.             //
  674.  
  675.             if (attributes & dragInsideSenderWindow) {
  676.                 if ((offset >= (**(theDocument->theTE)).selStart) &&
  677.                     (offset <= (**(theDocument->theTE)).selEnd)) {
  678.                         offset = -1;
  679.                 }
  680.             }
  681.  
  682.             if (hitDoc == theDocument) {
  683.  
  684.                 insertPosition = offset;
  685.  
  686.                 //
  687.                 //    Reset flashing counter if the offset has moved. This makes the
  688.                 //    caret blink only after the caret has stopped moving long enough.
  689.                 //
  690.  
  691.                 if (offset != lastOffset) {
  692.                     caretTime = theTime;
  693.                     caretShow = true;
  694.                 }
  695.                 lastOffset = offset;
  696.  
  697.                 //
  698.                 //    Flash caret.
  699.                 //
  700.  
  701.                 if (theTime - caretTime > gCaretTime) {
  702.                     caretShow = !caretShow;
  703.                     caretTime = theTime;
  704.                 }
  705.                 if (! caretShow) {
  706.                     offset = -1;
  707.                 }
  708.  
  709.                 //
  710.                 //    If caret offset has changed, move caret on screen.
  711.                 //
  712.  
  713.                 if (offset != caretOffset) {
  714.                     if (caretOffset != -1) {
  715.                         DrawCaret(caretOffset, theDocument->theTE);
  716.                     }
  717.                     if (offset != -1) {
  718.                         DrawCaret(offset, theDocument->theTE);
  719.                     }
  720.                 }
  721.  
  722.                 caretOffset = offset;
  723.  
  724.             } else {
  725.  
  726.                 lastOffset = offset;
  727.                 insertPosition = -1;
  728.  
  729.             }
  730.  
  731.             break;
  732.  
  733.         case dragTrackingLeaveWindow:
  734.  
  735.             //
  736.             //    If the caret is on the screen, remove it.
  737.             //
  738.  
  739.             if (caretOffset != -1) {
  740.                 DrawCaret(caretOffset, theDocument->theTE);
  741.                 caretOffset = -1;
  742.             }
  743.  
  744.             //
  745.             //    Remove window highlighting, if showing.
  746.             //
  747.  
  748.             if ((cursorInContent) && (attributes & dragHasLeftSenderWindow))
  749.                 HideDragHilite(theDrag);
  750.  
  751.             break;
  752.  
  753.         case dragTrackingLeaveHandler:
  754.             break;
  755.  
  756.     }
  757.  
  758.     return(noErr);
  759. }
  760.  
  761.  
  762. /*
  763.  *    DropLocationIsFinderTrash
  764.  *
  765.  *    Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  766.  */
  767.  
  768. Boolean DropLocationIsFinderTrash(AEDesc *dropLocation)
  769.  
  770. {    OSErr            result;
  771.     AEDesc            dropSpec;
  772.     FSSpec            *theSpec;
  773.     CInfoPBRec        thePB;
  774.     short            trashVRefNum;
  775.     long            trashDirID;
  776.  
  777.     //
  778.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  779.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  780.     //
  781.  
  782.     if ((dropLocation->descriptorType != typeNull) &&
  783.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) {
  784.  
  785.         HLock(dropSpec.dataHandle);
  786.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  787.  
  788.         //
  789.         //    Get the directory ID of the given dropLocation object.
  790.         //
  791.  
  792.         thePB.dirInfo.ioCompletion = 0L;
  793.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  794.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  795.         thePB.dirInfo.ioFDirIndex = 0;
  796.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  797.  
  798.         result = PBGetCatInfo(&thePB, false);
  799.  
  800.         HUnlock(dropSpec.dataHandle);
  801.         AEDisposeDesc(&dropSpec);
  802.  
  803.         if (result != noErr)
  804.             return(false);
  805.  
  806.         //
  807.         //    If the result is not a directory, it must not be the Trash.
  808.         //
  809.  
  810.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  811.             return(false);
  812.  
  813.         //
  814.         //    Get information about the Trash folder.
  815.         //
  816.  
  817.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  818.  
  819.         //
  820.         //    If the directory ID of the dropLocation object is the same as the directory ID
  821.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  822.         //
  823.  
  824.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  825.             return(true);
  826.     }
  827.  
  828.     return(false);
  829. }
  830.  
  831.  
  832. /*
  833.  *    DragText
  834.  *
  835.  *    Drag the selected text in the given document.
  836.  */
  837.  
  838. short DragText(Document *theDocument, EventRecord *theEvent, RgnHandle hiliteRgn)
  839.  
  840. {    short                result;
  841.     RgnHandle            dragRegion, tempRgn;
  842.     Point                theLoc;
  843.     DragReference        theDrag;
  844.     StScrpHandle        theStyl;
  845.     AEDesc                dropLocation;
  846.     DragAttributes        attributes;
  847.     short                mouseDownModifiers, mouseUpModifiers, copyText;
  848.  
  849.     //
  850.     //    Copy the hilite region into dragRegion and offset it into global coordinates.
  851.     //
  852.  
  853.     CopyRgn(hiliteRgn, dragRegion = NewRgn());
  854.     SetPt(&theLoc, 0, 0);
  855.     LocalToGlobal(&theLoc);
  856.     OffsetRgn(dragRegion, theLoc.h, theLoc.v);
  857.  
  858.     //
  859.     //    Wait for the mouse to move to the mouse button to be released. If the mouse button was
  860.     //    released before the mouse moves, return false. Returing false from DragText means that
  861.     //    a drag operation did not occur.
  862.     //
  863.  
  864.     if (! WaitMouseMoved(theEvent->where)) {
  865.         return(false);
  866.     }
  867.  
  868.     NewDrag(&theDrag);
  869.  
  870.     //
  871.     //    For purposes of demonstration, we insert the 'TEXT' data and promise 'styl'
  872.     //    data. If a receiver requests 'TEXT', the Drag Manager will give them the text
  873.     //    without needing us to intervene. If a receiver requests 'styl', the Drag Manager
  874.     //    will call our MySendDataProc to provide the data at drop time. The MySendDataProc
  875.     //    is specified by calling SetDragSendProc.
  876.     //
  877.  
  878.     AddDragItemFlavor(theDrag, 1, 'TEXT', GetSelectedTextPtr(theDocument),
  879.                       GetSelectionSize(theDocument), 0);
  880.  
  881.     theStyl = GetStylScrap(theDocument->theTE);
  882.     HLock((Handle) theStyl);
  883.     AddDragItemFlavor(theDrag, 1, 'styl', (Ptr) *theStyl, GetHandleSize((Handle) theStyl), 0);
  884.     HUnlock((Handle) theStyl);
  885.     DisposeHandle((Handle) theStyl);
  886.  
  887.     SetDragSendProc(theDrag, MySendDataProc, (void *) theDocument);
  888.  
  889.     //
  890.     //    Set the item's bounding rectangle in global coordinates.
  891.     //
  892.  
  893.     SetDragItemBounds(theDrag, 1, &(**dragRegion).rgnBBox);
  894.  
  895.     //
  896.     //    Prepare the drag region.
  897.     //
  898.  
  899.     tempRgn = NewRgn();
  900.     CopyRgn(dragRegion, tempRgn);
  901.     InsetRgn(tempRgn, 1, 1);
  902.     DiffRgn(dragRegion, tempRgn, dragRegion);
  903.     DisposeRgn(tempRgn);
  904.  
  905. #ifdef USE_CUSTOM_DRAWING
  906.  
  907.     SetDragDrawingProc(theDrag, MyDrawingProc, 0L);
  908.  
  909. #endif
  910.  
  911.     //
  912.     //    Drag the text. TrackDrag will return userCanceledErr if the drop zoomed-back
  913.     //    for any reason.
  914.     //
  915.  
  916.     result = TrackDrag(theDrag, theEvent, dragRegion);
  917.  
  918.     if (result != noErr && result != userCanceledErr) {
  919.         return(true);
  920.     }
  921.  
  922.     //
  923.     //    Check to see if the drop occurred in the Finder's Trash. If the drop occurred
  924.     //    in the Finder's Trash and a copy operation wasn't specified, delete the
  925.     //    source selection. Note that we can continute to get the attributes, drop location
  926.     //    modifiers, etc. of the drag until we dispose of it using DisposeDrag.
  927.     //
  928.  
  929.     GetDragAttributes(theDrag, &attributes);
  930.     if (!(attributes & dragInsideSenderApplication)) {
  931.  
  932.         GetDropLocation(theDrag, &dropLocation);
  933.  
  934.         GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  935.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  936.  
  937.         if ((!copyText) && (DropLocationIsFinderTrash(&dropLocation))) {
  938.             TEDelete(theDocument->theTE);
  939.             theDocument->dirty = true;
  940.         }
  941.  
  942.         AEDisposeDesc(&dropLocation);
  943.     }
  944.  
  945.     //
  946.     //    Dispose of the drag.
  947.     //
  948.  
  949.     DisposeDrag(theDrag);
  950.  
  951.     DisposeRgn(dragRegion);
  952.  
  953.     return(true);
  954. }
  955.  
  956.  
  957.  
  958.  
  959.